﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.Routing;
using MvcContrib.Pagination;

namespace TaskManager.Web.Extensions
{
    /// <summary>
    /// Provides extension method for rendering pagination control
    /// </summary>
    public static class PagerExtensions
    {
        /// <summary>
        /// Delegate for constructing Url for page button
        /// </summary>
        /// <param name="linkText">Link text</param>
        /// <param name="routeValues">Route values that should be added to query string</param>
        /// <returns>Url</returns>
        /// <remarks>Route values dictionary contains mandatory key 'page' which is page number</remarks>
        public delegate string BuildPageButtonUrl(string linkText, RouteValueDictionary routeValues);

        /// <summary>
        /// Renders HTML for showing Pager
        /// </summary>
        /// <param name="htmlHelper">Html helper instance</param>
        /// <param name="pagination">Pagination</param>
        /// <param name="maxButtonsCount">Maximum allowed buttons count</param>
        /// <param name="urlBuilder">Lambda expression for generating the URL used in the page links</param>
        /// <returns>Html</returns>
        /// <example>
        /// <code>
        /// <div class="pagination">
        ///     <ul>
        ///         <li class="prev disabled"><a href="#">← Previous</a></li>
        ///         <li class="active"><a href="#">1</a></li>
        ///         <li><a href="#">2</a></li>
        ///         <li><a href="#">3</a></li>
        ///         <li><a href="#">4</a></li>
        ///         <li><a href="#">5</a></li>
        ///         <li class="next"><a href="#">Next →</a></li>
        ///     </ul>
        /// </div>
        /// </code>
        /// </example>
        public static MvcHtmlString Pager(this HtmlHelper htmlHelper, IPagination pagination, int maxButtonsCount,
            BuildPageButtonUrl urlBuilder)
        {
            if (pagination.TotalItems == 0)
                return MvcHtmlString.Empty;

            Pager pager = CreatePager(pagination, maxButtonsCount);

            var routeValues = new RouteValueDictionary();
            const string pageParameter = "page";
            const string pageButtonClass = "pagerButton";

            // Add all values from query string
            foreach (var key in htmlHelper.ViewContext.RequestContext.HttpContext.Request.QueryString.AllKeys.Where(key => key != null))
            {
                routeValues[key] = htmlHelper.ViewContext.RequestContext.HttpContext.Request.QueryString[key];
            }

            if (pager.PageNumbers.Count == 0)
                return MvcHtmlString.Empty;

            var builder = new StringBuilder("<div class='pagination'>");
            builder.Append("<ul>");

            if (pager.Previous.Item1)
            {
                routeValues[pageParameter] = pager.Previous.Item2;
                string url = urlBuilder("← Previous", routeValues);
                builder.Append(string.Format("<li class=\"prev\">{0}</li>", url));
            }

            foreach(int page in pager.PageNumbers)
            {
                routeValues[pageParameter] = page;
                
                string url = urlBuilder(page.ToString(), routeValues);
                
                if(page == pagination.PageNumber)
                    builder.Append(string.Format("<li class=\"active {0}\">{1}</li>", pageButtonClass, url));
                else
                    builder.Append(string.Format("<li class=\"{0}\">{1}</li>", pageButtonClass, url));
            }

            if (pager.Next.Item1)
            {
                routeValues[pageParameter] = pager.Next.Item2;

                string url = urlBuilder("Next →", routeValues);
                builder.Append(string.Format("<li class=\"next {0}\">{1}</li>", pageButtonClass, url));
            }

            builder.Append("</ul>");
            builder.Append("</div>");

            return new MvcHtmlString(builder.ToString());
        }

        /// <summary>
        /// Creates collection of pager's buttons to render Pager for given <see cref="IPagination"/> object
        /// </summary>
        /// <param name="pagination">Pagination</param>
        /// <param name="maxButtonsCount">Maximum allowed button's count</param>
        /// <returns>Collection of pager's buttons</returns>
        public static Pager CreatePager(IPagination pagination, int maxButtonsCount)
        {
            var pager = new Pager();

            if (pagination.TotalItems == 0)
                return pager;

            if(pagination.TotalPages == 1)
                return pager;

            int buttonsCount;
            int startIndex;

            if(maxButtonsCount >= pagination.TotalPages)
            {
                buttonsCount = pagination.TotalPages;
                startIndex = 0;
                pager.Next = new Tuple<bool, int>(false, -1);
                pager.Previous = new Tuple<bool, int>(false, -1);
            }
            else
            {
                int groupNumber = (pagination.PageNumber-1)/maxButtonsCount;

                startIndex = groupNumber * maxButtonsCount;

                int itemsCount = (groupNumber + 1) * maxButtonsCount;

                if (itemsCount >= pagination.TotalPages) // Last group?
                {
                    buttonsCount = maxButtonsCount - (itemsCount - pagination.TotalPages);
                    pager.Next = new Tuple<bool, int>(false, -1);
                    pager.Previous = new Tuple<bool, int>(true, groupNumber * maxButtonsCount);
                }
                else
                {
                    buttonsCount = maxButtonsCount;

                    if(groupNumber == 0)    // First group?
                    {
                        pager.Previous = new Tuple<bool, int>(false, -1);
                        pager.Next = new Tuple<bool, int>(true, maxButtonsCount + 1);
                    }
                    else
                    {
                        pager.Previous = new Tuple<bool, int>(true, groupNumber * maxButtonsCount);
                        pager.Next = new Tuple<bool, int>(true, (groupNumber+1) * maxButtonsCount + 1);
                    }
                }
            }
            
            int endIndex = startIndex + buttonsCount;

            for (int page = startIndex; page < endIndex; page++ )
            {
                pager.PageNumbers.Add(page+1);
            }

            return pager;
        }
    }

    public class Pager
    {
        public Pager()
        {
            Previous = new Tuple<bool, int>(false,-1);
            Next = new Tuple<bool, int>(false, -1);
            PageNumbers = new List<int>();
        }

        public Tuple<bool, int> Previous { get; set; }
        public Tuple<bool, int> Next { get; set; }

        public IList<int> PageNumbers { get; private set; }
    }
}